import * as fs from 'fs';
import * as path from 'path';
import * as vscode from 'vscode';
import * as assert from 'assert';
import * as crypto from 'crypto';
import * as cheerio from 'cheerio';
import * as timeago from 'timeago.js';
import * as hljs from 'highlight.js';
import * as marked from 'marked';


class SnippetWebviewController {
  context?: vscode.ExtensionContext;

  openedPanels: Record<string, vscode.WebviewPanel | undefined> = {};

  hash?: string;

  init(context: vscode.ExtensionContext) {
    this.context = context;
  }

  private getIndexPath() {
    assert(this.context);
    return 'src/webview/public/index.html';
  }

  private replaceResources() {
    assert(this.context);
    const nonce = crypto.randomBytes(20).toString('hex');
    this.hash = nonce;
    return fs
      .readFileSync(path.join((this.context?.extensionPath || './'), this.getIndexPath()), 'UTF-8')
      .replace(/{{nonce}}/gm, this.hash)
  }

  private createPanel(snippet: RestCodeSnippet) {
    assert(this.context);
    const title = `${snippet.title.slice(0, 20)}...`;

    return vscode.window.createWebviewPanel('glWorkflow', title, vscode.ViewColumn.One, {
      enableScripts: true,
      localResourceRoots: [vscode.Uri.file(path.join((this.context?.extensionPath || './'), 'src'))],
      retainContextWhenHidden: true,
    });
  }

  async open(snippet: RestCodeSnippet) {
    const panelKey = `snippets-${snippet.id}`;
    const openedPanel = this.openedPanels[panelKey];
    if (openedPanel) {
      openedPanel.reveal();
      return openedPanel;
    }
    const newPanel = await this.create(snippet);
    this.openedPanels[panelKey] = newPanel;
    newPanel.onDidDispose(() => {
      this.openedPanels[panelKey] = undefined;
    });
    newPanel.webview.onDidReceiveMessage(
      async message => {
        switch (message.command) {
          case 'copySnippet':
            await this.copySnippet((snippet.files || []), message.text);
            break;
          case 'copyShareLink':
            await this.copyShareLink(snippet.web_url)
            break;
          default:
            break;
        }
      }
    );
    return newPanel;
  }

  private async create(snippet: RestCodeSnippet) {
    assert(this.context);
    const panel = this.createPanel(snippet);
    const html = this.replaceResources();

    panel.webview.html = this.fillInApp(html, this.getSnippetHtml(snippet));
    return panel;
  }


  getSnippetHtml(snippet: RestCodeSnippet): string {
    assert(this.context)
    const snippetStyle = fs.readFileSync(path.join((this.context?.extensionPath || './'), 'src/assets/styles/snippet.css'), 'UTF-8').toString()
    const html = `
      <style>${snippetStyle}</style>
      ${this.getSnippetHeaderHtml(snippet)}
      <h1 class="snippet_title">${snippet.title}</h1>
      <div class="snippet_desc md_preview">${this.marked(snippet.description)}</div>
      ${this.getSnippetFileHtml(snippet.files || [])}
      `
    return html
  }

  highlightCode(content: string) {
    assert(this.context);
    if (!content) return ''
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    const highlightContent = (hljs as any).highlightAuto(content).value;
    return this.generateCodeLineNum(highlightContent);
  }

  marked(content: string) {
    assert(this.context);
    if (!content) return ''
    marked.setOptions({
      breaks: false,
      gfm: true,
      langPrefix: "language-",
      // eslint-disable-next-line func-names
      'highlight': function (code) {
        // eslint-disable-next-line @typescript-eslint/no-explicit-any
        const html = (hljs as any).highlightAuto(code).value;
        return html;
      }
    });
    const html = marked(content); // 解析markdown
    return html
  }

  generateCodeLineNum(content: string) {
    assert(this.context);
    if (!content) return ''
    return `<ul><li>${content.replace(/\n/g, "\n</li><li>")}\n</li></ul>`
  }

  getSnippetHeaderHtml(snippet: RestCodeSnippet): string {
    assert(this.context)
    const {
      author: { avatar_url: avatar, name },
      updated_at: updatedAt,
      web_url: url
    } = snippet
    const html = `
      <header class="snippet_header">
        <img src="${avatar}">
        <span class="flex-1">
          ${timeago.format(updatedAt, 'zh_CN')}由 <b>${name}</b> 创建
        </span>
        <a href="${url}">在 CSDN 中打开并查看</a>
        <a href="javascript:void(0)" id="shareLink">分享</a>
        </header>

      <script nonce="${this.hash}">
        document.getElementById("shareLink").onclick = function() {
          vscode.postMessage({
            command: 'copyShareLink',
            text: ''
          })
        }
      </script>

        `
    // <a>编辑</a>
    // <a>删除</a>
    return html
  }

  getSnippetFileHtml(files: CodeSnippetFile[]): string {
    assert(this.context)
    const hightlightStyle = fs.readFileSync(path.join((this.context?.extensionPath || './'), 'src/assets/styles/highlight-dark.css'), 'UTF-8').toString()
    const mdStyle = fs.readFileSync(path.join((this.context?.extensionPath || './'), 'src/assets/styles/marked.css'), 'UTF-8').toString()
    return `
    <style>
    ${hightlightStyle}
    ${mdStyle}
    </style>
    <div id="snippet_list">
      ${files.map((f, i) => {
      const isMd = f.file_path.split('.').pop() === 'md'
      return `
          <div class="snippet_item_container">
            <div class="snippet_item_header">
              <div class="snippet_item_title">${f.file_path}</div>
              <div class="snippet_item_size">${f.size}字节</div>
              <a href="javascript:void(0)" data-index="${i}" class="snippet_item_copy">复制代码</a>
            </div>
            <div class="snippet_item_body">
              <div class="snippet_content">
                ${isMd ? `<div class="md_preview">${this.marked(f.content)}</div>` : `<pre><code class="hljs">${this.highlightCode(f.content)}</code></pre>`}
              </div>
            </div>
          </div>`
    }).join('')}
    </div>

    <script nonce="${this.hash}">
        const vscode = acquireVsCodeApi();
        document.getElementById("snippet_list").addEventListener("click", function(e) {
          const target = e.target || e.srcElement
          const snippetIndex = target.dataset.index
          vscode.postMessage({
            command: 'copySnippet',
            text: snippetIndex
          })
        })
    </script>
    `
  }

  async copySnippet(files: CodeSnippetFile[], index: number) {
    assert(this.context);
    const snippetContent = files[index]?.content
    await vscode.env.clipboard.writeText(snippetContent)
    await vscode.window.showInformationMessage('代码已复制到剪切板')
  }

  async copyShareLink(link: string) {
    assert(this.context);
    await vscode.env.clipboard.writeText(link)
    await vscode.window.showInformationMessage('分享链接已复制到剪切板')
  }

  fillInApp(templateHtml: string, html: string): string {
    assert(this.context)
    const $ = cheerio.load(templateHtml)
    $('div#app').html(html)
    return $.html()
  }
}

export const snippetWebviewController = new SnippetWebviewController();
